E.5 - Trasformazioni

Obiettivo
Rielaborare i dati geometrici di una o più parole in modo da ottenere una trasformazione/deformazione del testo visualizzato che deve rimanere almeno parzialmente riconoscibile.
Modalità
Creare uno sketch che compia le seguenti operazioni:
- caricare un font in formato TTF o OTF;
- scegliere una parola o una breve frase e ricavarne i vertici;
- a ogni fotogramma, trasformare i vertici ricavati;
- impostare l'ambiente grafico;
- disegnare una o più forme basate sui vertici.
Codice di base
let font; // font da utilizzare
let vettori = []; // vettori di base
function preload() {
font = loadFont( 'UbuntuCondensed.ttf' ); // carica il font
}
function setup() {
createCanvas( windowWidth, windowHeight );
let parametri = { sampleFactor: 1, simplifyThreshold: 0 };
let punti = font.textToPoints( 'parole', -350,50, 292, parametri );
for (let punto of punti) { // salva punti ricavati
vettori.push( createVector( punto.x, punto.y ) );
}
}
function draw() {
translate(width/2, height/2); // centra l'origine
let trasformati = []; // vettori trasformati
for (let i=0; i<vettori.length; ++i) { // TRASFORMA I VETTORI
let vettore = vettori[i].copy();
// PARTE PIÙ IMPORTANTE DEL CODICE
trasformati[i] = vettore;
}
background( 255, 24 ); // imposta ambiente grafico
noStroke();
fill( 0, 64 );
for (let i=0; i<trasformati.length; ++i) { // disegna elementi
let vert = trasformati[i];
circle(vert.x, vert.y, 2);
}
}
function windowResized() { // riadatta il canvas alla finestra
resizeCanvas( windowWidth, windowHeight );
}
ATTENZIONE!!!: Rispetto al codice di base, gli interventi devono riguardare SOLO le parti evidenziate in bianco e soprattutto quella del primo for del draw(). Le modifiche devono ovviamente andare oltre la semplice variazione di qualche parametro numerico e riguardare la logica vera e propria della trasformazione.
La risoluzione del canvas dovrà adattarsi a quella della finestra in cui viene eseguito lo sketch... ma è sufficiente lasciare invariate le parti non evidenziate in bianco.
La dimensione del font (4° parametro del metodo textToPoints()) può variare ma la larghezza del testo non deve superare i 700 pixel. Per rispettare la specifica è sufficiente fare in modo che la coordinata x (2° parametro di textToPoints()) non sia mai inferiore a -350 e che l'intero testo risulti centrato.
Sul p5.js Web Editor si può trovare una versione base che può essere duplicata (File / Duplicate) dopo aver fatto il login al sito. Ovviamente sarà poi necessario caricare un font TTF o OTF attraverso le stesse modalità usate per caricare l'anteprima. Su OpenProcessing si trova una variante che sfrutta il metodo rotate() dei vettori.
Consigli
Scelta del font
Ci sono molti siti da cui è possibile scaricare font anche gratuiti ma si consiglia di utilizzare i Google Fonts che permette una consultazione molto comoda di font utilizzabili più o meno liberamente [verificate sempre il tipo di licenza]. Fra le altre cose è possibile modificare il testo d'esempio dei font inserendone uno nuovo nel campo "Sentence".
Si consiglia di partire da un font semplice (ad esempio l'Open Sans o il Roboto, anche nelle versioni Condensed) per valutare prima l'effetto ottenuto. Una volta scelto l'effetto si può passare a un font che lo renda più efficace facendo attenzione a non sceglierne uno così particolare o già molto "trasformato" da rendere secondario tutto il resto.
Scelta delle parole
Valutato l'effetto conviene utilizzare una o più parole coerenti con le sensazioni evocate o che interagisca semanticamente con l'effetto ottenuto, anche per contrasto.
Se si vogliono utilizzare più righe, si possono duplicare il textToPixels() e il for() nel setup():
let parametri = { sampleFactor: 1, simplifyThreshold: 0 };
let punti = font.textToPoints( 'riga 1', -300,-50, 280, parametri );
for (let punto of punti) {
vettori.push( createVector( punto.x, punto.y ) );
}
punti = font.textToPoints( 'riga 2', -300,200, 280, parametri );
for (let punto of punti) {
vettori.push( createVector( punto.x, punto.y ) );
}
Maggiori informazioni sui parametri da utilizzare per il calcolo dei punti si trovano nella pagina del metodo p5.Font.textToPixels().
Trasformazioni vettoriali: singoli vertici
Per ottenere una trasformazione dei singoli vertici, e quindi della configurazione del modello di base, è necessario intervenire su una o tutte e due le coordinate cartesiane, ad esempio:
var vettore = vettori[i].copy();
vettore.x += ...; // aggiunta di un valore
vettore.y *= ...; // moltiplicazione per un valore (possibilmente basso)
trasformati[i] = vettore;
È possibile intervenire anche indirettamente su tutte le coordinate dei vertici usando le funzioni e i metodi della classe p5.Vector, ad esempio:
vettore.mult(scala); // allontana ('scala' superiore a 1.0) o
// avvicina ('scala' inferiore a 1.0)
// il vettore rispetto all'origine del canvas
Direttamente o indirettamente si possono usare anche le funzioni matematiche facendo riferimento all'oscillation sandobox e all'esempio d'uso spiegato nella quarta lezione.
Trasformazioni vettoriali: coordinate polari
Per controllare meglio le trasformazioni può essere utile sapere anche come ottenere le coordinate polari adattate allo spazio 2D di p5.js:
var angolo = vettore.heading(); // angolo di rotazione
var lunghezza = vettore.mag(); // distanza rispetto all'origine (0,0)
Dopo le modifiche delle coordinate polari è possibile ottenere il corrispondente vertice/vettore usando il metodo p5.Vector.fromAngle():
vettore = p5.Vector.fromAngle( angolo, lunghezza );
Se ci si vuole limitare a far ruotare un vertice attorno al punto 0,0 (origine) si può utilizzare direttamente il metodo p5.Vector.rotate(), ad esempio:
vettore.rotate( HALF_PI ); // rotazione di 90° (in senso orario)
Trasformazioni vettoriali: modifiche progressive
Per modificare progressivamente un parametro è possibile utilizzare un valore che oscilli fra un minimo e un massimo con l'avanzare dei fotogrammi. Grazie alla funzione sin() si può, ad esempio, far oscillare il valore delle coordinate y di 30 pixel (sia verso l'alto che verso il basso) con una velocità dipendente dal divisore di frameCount:
var tempoY = frameCount/5;
vettore.y += map( sin(tempoY), -1,1, -30,30 );
Per evitare che le modifiche agiscano nello stesso modo su tutti i vertici, senza generare un'effettiva trasformazione, è necessario usare anche un valore che sia diverso per ogni vertice, come l'indice i del for, un numero casuale (restituito da random() o noise()), il valore di un'altra coordinata (ad es. 'y' per calcolare 'x' o viceversa) oppure un numero ricavato dal vertice/vettore stesso, ad esempio le coordinate polari o anche solo la distanza dal centro:
var tempoY = frameCount/5 + vettore.mag()/200;
vettore.y += map( sin(tempoY), -1,1, -30,30 );
Orientamento degli elementi grafici
Se si vogliono orientare i singoli elementi grafici verso l'esterno, si possono ricavare le coordinate del vertice rispetto al centro del canvas per creare poi un vettore con lo stesso orientamento, ad esempio:
for (let j=0; j<trasformati.length; ++j) {
let vert = trasformati[j];
push();
translate( vert );
let vettoreOrientato = p5.Vector.fromAngle( vert.heading(), 50 );
line( 0, 0, vettoreOrientato.x, vettoreOrientato.y );
pop();
}
Per orientare gli elementi verso l'interno, è sufficiente far ruotare i segmento di PI radianti:
vettoreOrientato.rotate( PI ); // rotazione di 180°
Sul p5.js Web Editor è presente l'esempio completo.
Consegna
Lo sketch andrà consegnato, dopo l'aggiunta dell'anteprima (obbligatoria), seguendo le modalità indicate nel "compito" della Classroom del corso.
Prima di consegnare l'esercitazione si consiglia di verificare il caricamento dell'anteprima attraverso la pagina di verifica dello sketch e l'eventuale adattamento del canvas alla modifica delle dimensioni della finestra del browser.